home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (C) 1993, Silicon Graphics, Inc.
- * All Rights Reserved.
- */
- #define _KERNEL /* more typically set on compile line */
-
- #include <sys/param.h>
- #include <sys/systm.h>
- #include <sys/cpu.h>
- #include <sys/buf.h>
- #include <sys/user.h>
- #include <sys/cmn_err.h>
- #include <sys/edt.h>
-
- /* NOTE: this sample driver ignores the possiblity that
- * the board might be busy handling some earlier request.
- * any real device must deal with that possiblity, of course,
- * before changing the board registers..
- */
-
- /* these defines and structures would normally be in a seperate
- * header file */
-
- #define GBD_BOARD_ID 0x75
- #define GBD_MASK 0xff /* use 0xff if using only first byte
- * of ID word, use 0xffff if using
- * whole ID word
- */
-
- #define GBD_NUM_DMA_PGS 4 /* 0 for no hardware scatter/gather
- * support, else number of pages of scatter/gather
- * supported per request */
-
- #define GBD_NODMA 0 /* non-zero for PIO version of driver */
-
- #define GBD_MEMSIZE 0x8000
-
- /* command definitions */
- #define GBD_GO 1
-
- /* state definitions */
- #define GBD_SLEEPING 1
- #define GBD_DONE 2
-
- /* direction of DMA definitions */
- #define GBD_READ 0
- #define GBD_WRITE 1
-
- /* status defines */
- #define GBD_INTR_PEND 0x80
-
- /* "gbd" is device prefix; also in master.d/xxx file */
-
- /* devices interface to the board */
- struct gbd_device {
- int command;
- int count;
- int direction;
- off_t offset;
- unsigned *sgregisters; /* if scatter/gather supported */
- caddr_t startaddr; /* if no scatter/gather on board */
- unsigned status; /* errors, interrupt pending, etc. */
- };
-
-
- /* these are used for no scatter/gather case only, and assume
- * (since they aren't protected!) that the driver is completely
- * single threaded. */
- struct buf *gbd_curbp[2]; /* current buffer */
- caddr_t gbd_curaddr[2]; /* current address to transfer */
- int gbd_curcount[2];
- int gbd_totcount[2];
-
- /* pointer to on-board registers */
- volatile struct gbd_device *gbd_device[2];
-
- char *gbd_memory[2]; /* pointer to on-board memory */
-
- static int gbd_state[2]; /* flag for transfer state
- * (PIO driver) */
-
- void gbdintr(int);
-
- /* early device table initialization routine. The edt
- * structure is defined in edt.h.
- */
- gbdedtinit(struct edt *e)
- {
- int slot, val;
-
- /* Check to see if the device is present */
- if(badaddr_val(e->e_base, sizeof(int), &val) ||
- (val && GBD_MASK) != GBD_BOARD_ID) {
- if (showconfig)
- cmn_err (CE_CONT,
- "gbdedtinit: board not installed.");
- return;
- }
-
-
- /* figure out slot from base on VECTOR line in
- * system file */
- if(e->e_base == 0xBF400000)
- slot = GIO_SLOT_0;
- else if(e->e_base == 0xBF600000)
- slot = GIO_SLOT_1;
- else {
- cmn_err (CE_NOTE,
- "ERROR from edtinit: Bad base address %x\n", e->e_base);
- return;
- }
-
- #if IP12 /* for Indigo R3000, set up board as a
- * realtime bus master
- */
-
- setgioconfig(slot,0);
-
- #endif
-
- #if IP20 /* for Indigo R4000, set up board as a
- * realtime bus master
- */
-
- setgioconfig(slot,GIO64_ARB_EXP0_RT | GIO64_ARB_EXP0_MST);
-
- #endif
-
- #if IP22 /* for Indigo2, set up board as a pipelined,
- * realtime bus master
- */
-
- setgioconfig(slot,GIO64_ARB_EXP0_RT | GIO64_ARB_EXP0_MST);
-
- #endif
-
- /* Save the device addresses, because
- * they won't be available later. */
-
- gbd_device[slot == GIO_SLOT_0 ? 0 : 1] =
- (struct gbd_device *)e->e_base;
- gbd_memory[slot == GIO_SLOT_0 ? 0 : 1] =
- (char *)e->e_base2;
- setgiovector(GIO_INTERRUPT_1,slot,gbdintr,0);
- }
-
-
- #ifdef GBD_NODMA
-
- /* device write routine entry point (for character devices) */
- gbdwrite(dev_t dev)
- {
- int unit = minor(dev)&1;
- int size;
- int s;
-
- while (u.u_count > 0) {
- /* while there is data to transfer */
-
- /* Transfer no more than GBD_MEMSIZE bytes
- * to the device */
-
- size = (u.u_count <
- GBD_MEMSIZE ? u.u_count : GBD_MEMSIZE);
-
- /* decrements u.u_count while copying data */
- iomove(gbd_memory[unit], size, B_WRITE);
- if (u.u_error)
- break;
-
- /* prevent interrupts until we sleep */
- s = splgio1();
-
- /* Transfer is complete; start output */
- gbd_device[unit]->count = u.u_count;
- gbd_device[unit]->command = GBD_GO;
- gbd_state[unit] = GBD_SLEEPING;
- while (gbd_state[unit] != GBD_DONE) {
- sleep(&gbd_state[unit], PRIBIO);
- }
- /* restore the process level after waking up */
- splx(s);
- }
- }
-
-
- /* interrupt routine for PIO only board, just wake up
- * upper half of driver
- */
- void
- gbdintr(int unit)
- {
- /* read your board's registers to determine if
- * there are any errors or interrupts pending.
- * If no interrupts are pending, return without
- * doing anything.
- */
- if(!gbd_device[unit]->status & GBD_INTR_PEND)
- return;
-
- if (gbd_state[unit] == GBD_SLEEPING) {
- /* Output is complete; wake up top half
- * of driver, if it is waiting */
- gbd_state[unit] = GBD_DONE;
- wakeup(&gbd_state[unit]);
- }
-
- /* do anything else to board to tell it we are done
- * with transfer and interrupt here */
- }
-
- #else /* DMA version of driver */
-
- #if GBD_NUM_DMA_PGS > 0
-
- /* block device read/write entry point, if your board has
- * hardware scatter/gather DMA support.
- */
- gbdstrategy(struct buf *bp)
- {
- int unit = minor(bp->b_dev)&1;
- int npages;
- volatile unsigned *sgregisters;
- int i, v_addr;
-
- /* Get address of the scatter-gather registers */
- *sgregisters = gbd_device[unit]->sgregisters;
-
- /* Get the kernel virtual address of the data; note
- * b_dmaaddr may be NULL if the BP_ISMAPPED(bp) macro
- * indicates false; in that case, the field bp->b_pages
- * is a pointer to a linked list of pfdat structure
- * pointers; that saves creating a virtual mapping and
- * then decoding that mapping back to physical addresses.
- * BP_ISMAPPED will never be false for character devices,
- * only block devices.
- */
- if(!BP_ISMAPPED(bp)) {
- cmn_err(CE_WARN,
- "gbd driver can't handle unmapped buffers");
- bp->b_flags |= B_ERROR;
- iodone(bp);
- return;
- }
-
- v_addr = bp->b_dmaaddr;
-
- /* Compute number of pages received.
- * The dma_len field provides the number of pages to
- * map. Note that this may be larger than the actual
- * number of bytes involved in the transfer. This is
- * because the transfer may cross page boundaries,
- * requiring an extra page to be mapped. Limit to
- * number of scatter/gather registers on board.
- * Note that this sample driver doesn't handle the
- * case of requests > than # of registers!
- */
- npages = numpages (v_addr, bp->b_dmalen);
- /*
- * Provide the beginning byte offset and count to the
- * device.
- */
- gbd_device[unit]->offset =
- (unsigned int)bp->b_dmaaddr & (NBPC-1);
- if(npages > GBD_NUM_DMA_PGS) {
- npages = GBD_NUM_DMA_PGS;
- cmn_err(CE_WARN,
- "request too large, only %d pages max", npages);
- if(gbd_device[unit]->offset)
- gbd_device[unit]->count = NBPC -
- gbd_device[unit]->offset + (npages-1)*NBPC;
- else
- gbd_device[unit]->count = npages*NBPC;
- bp->b_resid = bp->b_count - gbd_device[unit]->count;
- }
- else
- gbd_device[unit]->count = bp->b_count;
-
- /* Translate the virtual address of each page to a
- * physical page number and load it into the next
- * scatter-gather register. The btoct(K) macro
- * converts the byte value to a page value after
- * rounding down the byte value to a full page.
- */
- for (i = 0; i < npages; i++) {
- *sgregisters++ = btoct(kvtophys(v_addr));
-
- /*
- /* Get the next virtual address to translate.
- * (NBPC is a symbolic constant for the page
- * size in bytes)
- */
-
- v_addr += NBPC;
- }
-
- if ((bp->b_flags & B_READ) == 0)
- gbd_device[unit]->direction = GBD_WRITE;
- else
- gbd_device[unit]->direction = GBD_READ;
- gbd_device[unit]->command = GBD_GO; /* start DMA */
-
- /* and return; upper layers of kernel wait for iodone(bp) */
- }
-
-
- /* not much to do in this interrupt routine, since we are
- * assuming for this driver that we can never have to do
- * multiple DMA's to handle the number of bytes requested...
- */
- void
- gbdintr(int unit)
- {
- int error;
-
- /* read your board's registers to determine if
- * there are any errors or interrupts pending.
- * If no interrupts are pending, return without
- * doing anything.
- */
- if(!gbd_device[unit]->status & GBD_INTR_PEND)
- return;
-
- if(error)
- bp->b_flags |= B_ERROR;
-
- iodone(bp); /* we are done, tell upper layers */
-
- /* do anything else to board to tell it we are done
- * with transfer and interrupt here */
- }
-
- #else /* GBD_NUM_DMA_PGS == 0; no hardware
- * scatter/gather support */
-
- gbdstrategy(struct buf *bp)
- {
- int unit = minor(bp->b_dev)&1;
-
- /* any checking for initial state here. */
-
- /* Get the kernel virtual address of the data; note
- * b_dmaaddr may be NULL if the BP_ISMAPPED(bp) macro
- * indicates false; in that case, the field bp->b_pages
- * is a pointer to a linked list of pfdat structure
- * pointers; that saves creating a virtual mapping and
- * then decoding that mapping back to physical addresses.
- * BP_ISMAPPED will never be false for character devices,
- * only block devices.
- */
- if(!BP_ISMAPPED(bp)) {
- cmn_err(CE_WARN,
- "gbd driver can't handle unmapped buffers");
- bp->b_flags |= B_ERROR;
- iodone(bp);
- return;
- }
-
- gbd_curbp[unit] = bp;
- /*
- * Initialize the current transfer address and count.
- * The first transfer should finish the rest of the
- * page, but do no more than the total byte count.
- */
- gbd_curaddr[unit] = bp->b_dmaaddr;
- gbd_totcount[unit] = bp->b_count;
- gbd_curcount[unit] = NBPC -
- ((unsigned int)gbd_curaddr[unit] & (NBPC-1));
- if (bp->b_count < gbd_curcount[unit])
- gbd_curcount[unit] = bp->b_count;
- /* Tell the device starting physical address, count,
- * and direction */
- gbd_device[unit]->startaddr = kvtophys(gbd_curaddr[unit]);
- gbd_device[unit]->count = gbd_curcount[unit];
- if (bp->b_flags & B_READ) == 0)
- gbd_device[unit]->direction = GBD_WRITE;
- else
- gbd_device[unit]->direction = GBD_READ;
- gbd_device[unit]->command = GBD_GO; /* start DMA */
-
- /* and return; upper layers of kernel wait for iodone(bp) */
- }
-
-
- /* more complicated interrupt routine, not necessarily because
- * board has DMA, but more typical of boards that do have
- * DMA, since they are typically more complicated.
- * Also more typical of devices that support block i/o, as
- * opposed to character i/o.
- */
- void
- gbdintr(int unit)
- {
- int error;
- register struct buf *bp = gbd_curbp[unit];
-
- /* read your board's registers to determine if
- * there are any errors or interrupts pending.
- * If no interrupts are pending, return without
- * doing anything.
- */
- if(!gbd_device[unit]->status & GBD_INTR_PEND)
- return;
-
- if(error) {
- bp->b_flags |= B_ERROR;
- iodone(bp); /* we are done, tell upper layers */
- }
- else {
- /* On successful transfer of last chunk, continue
- * if necessary */
- gbd_curaddr[unit] += gbd_curcount[unit];
- gbd_totcount[unit] -= gbd_curcount[unit];
- if(gbd_totcount[unit] <= 0)
- iodone(bp);
- /* we are done, tell upper layers */
- else {
- /* else more to do, reprogram board and
- * start next dma */
- gbd_curcount[unit] =
- (gbd_totcount[unit] < NBPC
- ? gbd_totcount[unit] : NBPC);
- gbd_device[unit]->startaddr =
- kvtophys(gbd_curaddr[unit]);
- gbd_device[unit]->count = gbd_curcount[unit];
- if (bp->b_flags & B_READ) == 0)
- gbd_device[unit]->direction = GBD_WRITE;
- else
- gbd_device[unit]->direction = GBD_READ;
- gbd_device[unit]->command = GBD_GO;
- /* start next DMA */
- }
- }
-
- /* do anything else to board to tell it we are done
- * with transfer and interrupt here */
- }
- #endif /* GBD_NUM_DMA_PGS */
-
- #endif /* GBD_NODMA */
-
-